[afl-training] harness
harness 的名词含义是马具,动词含义是给马套上马具,引申为利用,治理。此处的意思或许应该理解为,如何写好一个 harness(马具)来使用 AFL。
之前在做 quickstart 的时候,没有使用 afl-clang-fast 进行编译,而是使用的 afl-clang,刚刚搜到 afl-clang-fast 的话,需要编译 llvm-mode。
此次的章节是讲的如何写 harness 让 AFL 测试代码片段。如果对 AFL 如何将数据发送到目标程序执行比较熟悉的话,可以跳过这一个章节,直接到 challenge 进行实战,如下的图描述了 AFL 的基本流程和模块间的关系。
- input:input 文件夹存放初始的种子,高质量的种子文件非常重要
- queue:从 queue 中读取内容作为程序输入,如果突变后的输入可以触发新的状态变化,将变异后的输入重新放入 queue 中
- crash:crash 存放触发 crashes 的输入
在 library.h 的这个库中,主要的功能是提供输入数据并计算得到输出。假如要测试如下的代码,该如何进行测试?
1 |
|
fuzz 库的单输入函数
fuzz 需要的准备工作有以下三点:
- 代码是可以正常运行的
- 需要插桩,来让 AFL 进行高效运行
- 需要将 fuzzer 生成的数据送入到测试库中,因此,我们必须写一个程序将外部输入送入到测试库中,这可以直接从文件中读取或者从标准输入中读取。
为了测试 library.h 库中的函数,那么额外写一个文件 harness.c,其中 main 函数来调用库中的函数,如下:
1 |
|
然后使用下面的命令进行编译,可以看到一共在 20 个地方进行了插桩:
1 | AFL_HARDEN=1 afl-clang-fast harness.c library.c -o harness |
创建 input 文件夹,并在其中提供初始化的种子文件,如果就按照上面编译的方式直接 afl-fuzz,会发现 AFL 提示报错:odd, check syntax!
在 harness 可执行文件中,调用了 library.h 库中的函数,但是没有设置 hook 使得 AFL 变异产生的数据输入到目标库函数中,因此,运行 afl-fuzz 就会抛出一个警告,没有发生任何的事!因此,我们需要修改 harness 代码,使其从标准输入 STDIN 中获取输入,并且将输入数据喂给目标函数,将 harness.c 修改如下:
新增了 read 函数从标准输入 STDIN 中读取数据到缓冲区 input 中,然后喂给 lib_echo 函数运行,也就是对 lib_echo 函数进行 fuzz:
1 |
|
然后重新使用 afl-clang-fast 插桩,再使用 afl-fuzz 运行,现在就可以正常被 fuzz 了,并且产生了 crashes。明确一点,AFL 产生的输入是直接通过标准输入 STDIN 传递。
fuzz 库的任意输入函数
如果要测试 lib_mul(int x, int y) 函数,这个函数需要两个输入,而且是两个数字。作者提供的 harness 如下,通过两个 read 函数从 STDIN 标准输入中来传递数据到 harness 可执行文件中。代码修改如下:
1 |
|
重新使用 afl-clang-fast 编译,使用 afl-fuzz 进行 fuzz,但是这个时候需要带参数 mul 运行 harness,并且还需要在初始输入提供一个高质量:两个回车(或者 /0)分隔的整数:
1 | 20 |
1 | AFL_HARDEN=1 afl-clang-fast harness.c library.c -o harness |
应该是 fuzz 不出来什么结果了,因为 lib_mul 函数内部是数字运算,触发不了什么异常。
练习
作者留下了一个练习,如果有一个程序是从 argv 读取文件名,然后读取文件内容到缓冲区,并且将缓冲区传递到目标函数中,那么该如何对这个程序进行 fuzz。
这个部分我没有理解到,是要使用 AFL 构建文件内容然后进行 fuzz 么?